package ms.jasim.pddl;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import ms.jasim.model.JasimModel;
import ms.utils.INamedList;
import ms.utils.NamedList;

public class PddlProblem {
	
	public static final String METRIC_PLAN_QUALITY = "maximize (rt_plan_quality)"; 
	
	public final INamedList<PddlObject> objects;
	public final List<PddlPredicate> goals;
	public final List<PddlPredicate> facts;
	
	private String metric;

	private JasimModel model;
	
	public PddlProblem(JasimModel model) {
		objects = new NamedList<PddlObject>() {
			private static final long serialVersionUID = 1L;
			private Hashtable<String, PddlObject> hash;
			
			@Override
			public boolean add(PddlObject e) {
				if (this.hash == null)
					this.hash = new Hashtable<String, PddlObject>();
				hash.put(e.getName(), e);
				return super.add(e);
			}
			
			@Override
			public PddlObject get(String name) {
				if (this.hash != null && hash.containsKey(name))
					return hash.get(name);
				return super.get(name);
			}
		};
		goals = new ArrayList<PddlPredicate>();
		facts = new ArrayList<PddlPredicate>();
		metric = METRIC_PLAN_QUALITY;
		this.model = model;
	}
	
	@Override
	public String toString() {
		StringBuilder res = new StringBuilder();
		
		res.append(		"(define (problem ptool)\r\n" + 
						"	(:domain tropos-domain)\r\n" +
						"	(:objects\r\n");
	
		Collections.sort(objects);
		PddlObjectType last = PddlObjectType.T_UNKNOWN;
		int objInALine = 0;
		for (Iterator<PddlObject> iterator = objects.iterator(); iterator.hasNext();) {
			PddlObject obj = iterator.next();
			if (last != obj.getType() || objInALine == 10) {
				if (last !=  PddlObjectType.T_UNKNOWN )
					res.append(" - " + last.toString().toLowerCase() + "\r\n\t\t");
				else 
					res.append("\t\t");
				objInALine = 0;
			}
			objInALine++;
			last = obj.getType();
			res.append(obj.getQuanlifyName());
			res.append(iterator.hasNext()? " ": (" - " + last.toString().toLowerCase() + "\r\n\t)\r\n"));
		}
		
		res.append("	(:goal\r\n");
		res.append("		(and\r\n");
		for(PddlPredicate p: goals)
			res.append("\t\t" + p.toString() + "\r\n");
		res.append("		)\r\n" +
				   "	)\r\n");
		
		Collections.sort(facts);
		res.append("	(:init\r\n");
		for (PddlPredicate p: facts)
			res.append("\t\t" + p.toString() + "\r\n");
		res.append("	)\r\n");
		
		if (metric != null && metric.length() > 0)
			res.append(String.format("\t(:metric %s)\r\n", metric));
		res.append(")\r\n");
		return res.toString();
	}
	
	public void writeToFile(String filename) throws IOException {
		FileWriter fw = new FileWriter(filename, false);
		fw.write(this.toString());
		fw.close();
	}

	public void setMetric(String metric) {
		this.metric = metric;
	}

	public String getMetric() {
		return metric;
	}

	public JasimModel getModel() {
		return this.model;
	}

	public PddlPredicate findPredicate(List<PddlPredicate> predicateList, PddlPredicate pred, int flags) {
		for (PddlPredicate p: predicateList)  
			if (p.toString().equalsIgnoreCase(pred.toString()))
				return p;
			else if (flags > 0 && p.getFunctor() == pred.getFunctor() && p.getArgs().size() == pred.getArgs().size()) {
				int argMatched = 0;
				for (int i = 0; i < p.getArgs().size(); i++) {
					Object predArg = pred.getArgs().get(i);
					Object pArg = p.getArgs().get(i);
					if (predArg != null)
						argMatched += pArg.toString().equalsIgnoreCase(predArg.toString()) ? 1: 0;
				}
				if (argMatched == p.getArgs().size())
					return p;
			}
		return null;
	}

	public PddlObject findObject(PddlObjectType type, String name, int instanceID) {
		for (PddlObject obj: objects) 
			if (obj.getType() == type && obj.getName().equalsIgnoreCase(name) 
					&& obj.getInstanceID() == instanceID)
				return obj;
		return null;
	}
}
